/*
 * BitFileManager.cpp
 *
 *  Created on: May 24, 2012
 *      Author: Nico
 */

#ifndef BITFILEMANAGER_H_
#define BITFILEMANAGER_H_

#include "includes.h"
#include "BitFileManager.h"

OBitFileManager::OBitFileManager(const char* outputFilename){
	this->outputFilename = (char*)outputFilename;
	
	this->completeToByteLastChar = true;
	this->completeToByte = true;
	this->clearBuffer = false;
	this->amountLastBitsReaded = 0;
	
	this->rellenoAplicado = false;
	
	this->buffer = 0;
	 
	this->output = new ofstream( outputFilename , ios::binary );
	
	this->debug = false;
}

OBitFileManager::~OBitFileManager(){
	this->completarConUnoYceros();
	this->output->close();
	delete this->output;
}

/* protected */ int OBitFileManager::completarConUnoYceros(){
	short cont = this->amountLastBitsReaded;
	
	if(this->rellenoAplicado == true) return 0;
	
	if(this->debug){
		printf("\nProcedo a completar con 1 (obligatorio) y tantos 0 necesarios para completar a byte (puede no haber ningun 0):\n");
	}
	
	this->buffer = (this->buffer << 1) | 1; // LeftShift bit 1 (agrego bit 1 a la izquierda)
	if(this->debug) printf("Completo con: '1");
	cont++;
	
	while( cont < 8 ){
		this->buffer = this->buffer << 1; if(this->debug) printf("0");
		cont++;
	}
	
	if(this->debug) printf("'\n");
	*this->output << this->buffer;

	
	
	if(this->debug){
		char* bin_debug = (char*) binary( (int)this->buffer ).c_str();
		printf("\nEscribo en archivo: '%s' ; En hexa: %02X\n\n",bin_debug,this->buffer);
	}

	this->amountLastBitsReaded = 0;
	this->rellenoAplicado = true;
	return 0;
}

int OBitFileManager::writeBinaryFileFromBinaryRepresentationString(const char* binaryRepresentationString ){
	char* charIterator = (char*)binaryRepresentationString;
	
	short cont = 0; 
	char* bin_debug;
	
	this->rellenoAplicado = false;
	
	if( this->clearBuffer == true ){
		// Inicializo en cero.
		this->buffer = 0;
	}else{
		// Tengo que saber cuantos bits fueron leidos anteriormente
		// con el buffer que me estan pasando.
		cont = this->amountLastBitsReaded;
		//if(this->debug) printf("Cantidad leida en buffer: %d\n",cont); 
	}
	
	if(this->debug) printf("Leido: '");
	
	while( *charIterator != '\0' ){
		if( *charIterator == ' ' ){
			charIterator++; continue;
		}
		// Hago un LeftShift (agrego bit 0 a la izquierda)
		this->buffer = this->buffer << 1; 
		
		if( *charIterator == '1' ){
			// El 0 agregado lo convierte en 1
			this->buffer = this->buffer | 1; 
			if(this->debug) printf("1");
		}else{
			if(this->debug) printf("0");
		}
		
		charIterator++;
		cont++;
		if(cont == 4) if(this->debug) printf("-");
		if(cont == 8){
			
			if(this->debug){
				bin_debug = (char*) binary( (int)this->buffer ).c_str();
				printf("'\nEscribo en archivo: '%s' ; En hexa: %02X\n\n",bin_debug,this->buffer);
				printf("Leido: '");
			}
			
			*this->output << this->buffer;
			this->buffer = 0;
			cont = 0;			
		}
	}
	this->amountLastBitsReaded = cont;
	
	if(this->debug) printf("'    -BuffAmount: %d\n",cont);
	
	if( this->completeToByte == true ){
		return this->completarConUnoYceros();
	}
	
	return cont;
}

int OBitFileManager::writeBinaryFileFromTextString(const char* textString){
	char* charIterator = (char*)textString;
	uInt length;

	this->rellenoAplicado = false;

	if(this->completeToByteLastChar) length = strlen(textString);
	while( *charIterator != '\0' ){
		string binario = binary( (int)*charIterator );
		
		if(this->completeToByteLastChar){
			if( length == 1 ){
				this->completeToByte = !this->completeToByte;
				this->amountLastBitsReaded = writeBinaryFileFromBinaryRepresentationString( binario.c_str() );
				this->completeToByte = !this->completeToByte;
			}else{
				this->amountLastBitsReaded = writeBinaryFileFromBinaryRepresentationString( binario.c_str() );
			}
			length--;
		}else{
			this->amountLastBitsReaded = writeBinaryFileFromBinaryRepresentationString( binario.c_str() );
		}
		
		charIterator++;
	}
	
	return this->amountLastBitsReaded;
}

// El parametro es un string de la forma "100110101001", osea, solo
// caracteres '1' y '0' que representan un binario de algo.
int OBitFileManager::writeBinaryString(const char* binaryString){
	this->clearBuffer = false;
	this->completeToByte = false;
	return this->writeBinaryFileFromBinaryRepresentationString( binaryString );
}
int OBitFileManager::writeBinaryString(const string* binaryString){
	return this->writeBinaryString( binaryString->c_str() );
}


// El parametro es un string normal, una cadena de chars.
int OBitFileManager::writeText(const char* textString){
	this->completeToByte = false;
	this->completeToByteLastChar = false;
	this->clearBuffer = false;
	return this->writeBinaryFileFromTextString( textString );
}

int OBitFileManager::writeText(const string* textString){
	return this->writeText( textString->c_str() );
}





IBitFileManager::IBitFileManager(const char* inputFilename){
	this->inputFilename = (char*)inputFilename;
	
	this->buffer = 0;
	
	this->amountLastBitsReaded = 0;
	
	this->input = new ifstream( inputFilename , ios::binary );
	
	// Cantidad de bytes que leo del archivo en cada iteracion
	this->blockSize = 500;
	this->textBlock = new uChar[this->blockSize];
	*this->textBlock = '\0'; 
	this->blockIterator = this->textBlock;
	
	this->debug = false;
}
IBitFileManager::~IBitFileManager(){
	delete[] this->textBlock;
	this->input->close();
	delete this->input;
}

/* protected */ uChar IBitFileManager::getBit(uChar* aChar , uInt bitNumber){
        uChar mask;
        
        switch( bitNumber ){
			case 1:
				mask = 0x80; // 1000 0000
				break;
			
			case 2:
				mask = 0x40; // 0100 0000
				break;
			
			case 3:
				mask = 0x20; // 0010 0000
				break;
			
			case 4:
				mask = 0x10; // 0001 0000
				break;
			
			case 5:
				mask = 0x08; // 0000 1000
				break;
			
			case 6:
				mask = 0x04; // 0000 0100
				break;
			
			case 7:
				mask = 0x02; // 0000 0010
				break;
			
			case 8:
				mask = 0x01; // 0000 0001
				break;
		}

        if( mask == ( mask & *aChar ) ){
			return '1';
        }else{
			return '0';
		}
        
}

int IBitFileManager::getNextBit(uChar* bit){
	*bit = '\0';
	if( this->amountLastBitsReaded == 0 ){
		
		if( *this->blockIterator == '\0' ){
			
			
			// No entiendo muy bien este eof, hace cosas raras
			// por eso lo pongo 2 veces
			if( this->input->eof() ) return ERROR_EOF;
			
			if(this->debug) cout << "Leyendo otro bloque de texto\n";
			*this->input >> this->textBlock;
			
			//if( this->input->eof() ) return ERROR_EOF;
			
			this->blockIterator = this->textBlock;
			
			if(this->debug) printf( "this->textBlock: '%s'\n",this->textBlock);
		}
	}

	// Leo 1 bit
	this->amountLastBitsReaded++;
	
	if(this->debug) printf( "\n\nthis->amountLastBitsReaded: %d (Estoy leyendo bit %d)\n", this->amountLastBitsReaded, this->amountLastBitsReaded);
	if(this->debug) printf( "this->blockIterator: '%c'\n",*this->blockIterator);
	
	*bit = this->getBit( (uChar*)this->blockIterator , this->amountLastBitsReaded);
	
	if(this->amountLastBitsReaded == 8){
		this->amountLastBitsReaded = 0;
		this->blockIterator++;
	}
	return 0;
}

int IBitFileManager::getNextBits(const uInt amount, string* bits){
	bits->clear(); 
	uChar byte;
	for(uInt i=0 ; i < amount ; i++){
		if( ERROR_EOF == this->getNextBit(&byte) ) return ERROR_EOF;
		bits->push_back(byte);
	}
	return 0;
}

int IBitFileManager::getNextByte(uChar* byte){
	uChar bit = '2';
	uChar myByte = 0x00;

	for( short i = 0 ; i<8 ; i++ ){
		if( ERROR_EOF == this->getNextBit(&bit) ) return ERROR_EOF;
		
		myByte = myByte << 1;
		if( bit == '1' ){
			myByte = myByte | 1;
		}
	}
	*byte = myByte;

	return 0;
}

int IBitFileManager::getNextBytes(const uInt amount, string* bytes){
	bytes->clear(); 
	uChar byte;
	for(uInt i=0 ; i < amount ; i++){
		if( ERROR_EOF == this->getNextByte(&byte) ) return ERROR_EOF;
		bytes->push_back(byte);
	}
	return 0;
}

#endif /* BITFILEMANAGER_H_ */
